﻿using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using RainEngine.Rendering;
using Microsoft.Xna.Framework.Content;
using System;

namespace RainEngine.Effects
{
    /// <summary>
    /// 支持光照的Effect封装类从这个类继承，但这个类不包含高光颜色和高光强度
    /// </summary>
    public abstract class DiffuseLightEffectWrapper : IEffectMatrices ,IEffectFog 
    { 
        #region Effect参数

        /// <summary>
        /// 类所包装的Effect类
        /// </summary>
        protected internal Effect effect;

        /// <summary>
        /// effect的参数集合
        /// </summary>
        protected internal EffectParameterCollection parameters;

        protected EffectParameter worldParam;
        protected EffectParameter worldViewProjParam;
        protected EffectParameter worldInverseTransposeParam; 
        protected EffectParameter eyePositionParam;

        protected  EffectParameter diffuseColorParam;
        protected  EffectParameter emissiveColorParam;

        protected EffectParameter fogColorParam;
        protected EffectParameter fogVectorParam;
        
        protected  EffectParameter light0Param;
        protected  EffectParameter light1Param;
        protected  EffectParameter light2Param;
        
        protected  EffectParameter shaderIndexParam;

        protected EffectParameter clipPlaneEnabledParam;
        protected EffectParameter clipPlaneParam;       
        
        #endregion

        #region 字段

        protected Matrix world = Matrix.Identity;
        protected Matrix view = Matrix.Identity;
        protected Matrix projection = Matrix.Identity;
        protected Matrix worldView;
        protected bool clipPlaneEnabled = false;
        
        protected float fogStart = 0;
        protected float fogEnd = 1;
        protected bool fogEnabled = false;

        protected Vector3 diffuseColor = Vector3.One;
        protected Vector3 emissiveColor = Vector3.Zero;
        protected Vector3 ambientLightColor = Vector3.Zero;

        protected float alpha = 1;
        
        protected bool lightingEnabled;

        protected internal EffectDirtyFlags dirtyFlags = EffectDirtyFlags.All;

        #endregion

        #region 属性

        /// <summary>
        /// 获取Effect中的Technique集合
        /// </summary>
        public EffectTechniqueCollection Techniques
        {
            get { return effect.Techniques; }
        }

        /// <summary>
        /// 获取或设置世界矩阵。世界矩阵改变时需要重新设置effect的World，WorldViewProj,Fog相关参数
        /// </summary>
        public Matrix World
        {
            get { return world; }
            set
            {
                world = value;
                dirtyFlags |= EffectDirtyFlags.World | EffectDirtyFlags.WorldViewProj | EffectDirtyFlags.Fog;
            }
        }

        /// <summary>
        /// 获取或设置，视矩阵视矩阵改变时需要重新设置effect的WorldViewProj.Fog的相关参数。 
        /// </summary>
        public Matrix View
        {
            get { return view; }
            set
            {
                view = value;
                dirtyFlags |= EffectDirtyFlags.WorldViewProj | EffectDirtyFlags.Fog;
            }
        }

        /// <summary>
        /// 获取或设置投影矩阵，投影矩阵改变时需要重新设置effect的WorldViewProj。
        /// </summary>
        public Matrix Projection
        {
            get { return projection; }
            set
            {
                projection = value;
                dirtyFlags |= EffectDirtyFlags.WorldViewProj;
            }
        }

        /// <summary>
        /// 获取或设置相机位置
        /// </summary>
        public virtual Vector3 EyePosition
        {
            get { return eyePositionParam.GetValueVector3(); }
            set { eyePositionParam.SetValue(value); }
        }

        /// <summary>
        /// 获取或设置剪裁平面，将这个值设置为Vector.Zero就关闭剪裁
        /// </summary>
        public Vector4 ClipPlane
        {
            get { return clipPlaneParam.GetValueVector4(); }
            set
            {
                if (value == Vector4.Zero)
                    clipPlaneEnabled = false;
                else
                    clipPlaneEnabled = true;
                clipPlaneEnabledParam.SetValue(clipPlaneEnabled);
                clipPlaneParam.SetValue(value);
            }
        }
        
        /// <summary>
        /// 获取或设置漫反射颜色。
        /// </summary>
        public Vector3 DiffuseColor
        {
            get { return diffuseColor; }
            set
            {
                diffuseColor = value;
                dirtyFlags |= EffectDirtyFlags.MaterialColor;
            }
        }

        /// <summary>
        /// 获取或设置自发光颜色。
        /// </summary>
        public Vector3 EmissiveColor
        {
            get { return emissiveColor; }
            set
            {
                emissiveColor = value;
                dirtyFlags |= EffectDirtyFlags.MaterialColor;
            }
        }

        /// <summary>
        /// 获取或设置材质的alpha通道。
        /// </summary>
        public float Alpha
        {
            get { return alpha; }
            set
            {
                alpha = value;
                dirtyFlags |= EffectDirtyFlags.MaterialColor;
            }
        }        

        /// <summary>
        /// 获取或设置是否使用光照。
        /// </summary>
        public bool LightingEnabled
        {
            get { return lightingEnabled; }

            set
            {
                if (lightingEnabled != value)
                {
                    lightingEnabled = value;
                    dirtyFlags |= EffectDirtyFlags.ShaderIndex | EffectDirtyFlags.MaterialColor;
                }
            }
        }

        /// <summary>
        /// 获取或设置环境光颜色。
        /// </summary>
        public Vector3 AmbientLightColor
        {
            get { return ambientLightColor; }

            set
            {
                ambientLightColor = value;
                dirtyFlags |= EffectDirtyFlags.MaterialColor;
            }
        }

        /// <summary>
        /// 获取或设置是否开启雾化。
        /// </summary>
        public bool FogEnabled
        {
            get { return fogEnabled; }
            set
            {
                if (fogEnabled != value)
                {
                    fogEnabled = value;
                    dirtyFlags |= EffectDirtyFlags.ShaderIndex | EffectDirtyFlags.FogEnable;
                }
            }
        }

        /// <summary>
        /// 获取或设置雾化开始距离。
        /// </summary>
        public float FogStart
        {
            get { return fogStart; }
            set
            {
                fogStart = value;
                dirtyFlags |= EffectDirtyFlags.Fog;
            }
        }


        /// <summary>
        /// 获取或设置雾化结束距离。
        /// </summary>
        public float FogEnd
        {
            get { return fogEnd; }
            set
            {
                fogEnd = value;
                dirtyFlags |= EffectDirtyFlags.Fog;
            }
        }

        /// <summary>
        /// 获取或设置雾化颜色。
        /// </summary>
        public Vector3 FogColor
        {
            get { return fogColorParam.GetValueVector3(); }
            set { fogColorParam.SetValue(value); }
        }

        public Light Light0
        {
            set
            {
                light0Param.StructureMembers["enabled"].SetValue(value.Enabled);
                light0Param.StructureMembers["color"].SetValue(value.Color);
                light0Param.StructureMembers["position"].SetValue(value.Position);
                light0Param.StructureMembers["direction"].SetValue(value.Direction);
                light0Param.StructureMembers["lightType"].SetValue((float)value.LightType);
                light0Param.StructureMembers["spotData"].SetValue(value.SpotData);
            }
        }

        public Light Light1
        {
            set
            {
                light1Param.StructureMembers["enabled"].SetValue(value.Enabled);
                light1Param.StructureMembers["color"].SetValue(value.Color);
                light1Param.StructureMembers["position"].SetValue(value.Position);
                light1Param.StructureMembers["lightType"].SetValue((float)value.LightType);
                light1Param.StructureMembers["direction"].SetValue(value.Direction);
                light1Param.StructureMembers["spotData"].SetValue(value.SpotData);
            }
        }

        public Light Light2
        {
            set
            {
                light2Param.StructureMembers["enabled"].SetValue(value.Enabled);
                light2Param.StructureMembers["enabled"].SetValue(value.Enabled);
                light2Param.StructureMembers["color"].SetValue(value.Color);
                light2Param.StructureMembers["position"].SetValue(value.Position);
                light2Param.StructureMembers["lightType"].SetValue((float)value.LightType);
                light2Param.StructureMembers["direction"].SetValue(value.Direction);
                light2Param.StructureMembers["spotData"].SetValue(value.SpotData);
            }
        }         

        #endregion

        public DiffuseLightEffectWrapper(Effect setEffect)
        {
            if (setEffect == null)
            {
                throw new ArgumentNullException("setEffect");
            }
            effect = setEffect.Clone();
            parameters = effect.Parameters;
            CacheEffectParameters();
        }

        /// <summary>
        /// 缓存effect的参数。
        /// </summary>
        protected virtual void CacheEffectParameters()
        {
            worldParam = parameters["World"];
            worldViewProjParam = parameters["WorldViewProj"];
            eyePositionParam = parameters["EyePosition"];
            worldInverseTransposeParam = parameters["WorldInverseTranspose"];

            diffuseColorParam = parameters["DiffuseColor"];
            emissiveColorParam = parameters["EmissiveColor"];

            fogColorParam = parameters["FogColor"];
            fogVectorParam = parameters["FogVector"];

            shaderIndexParam = parameters["ShaderIndex"];

            light0Param = parameters["Lights"].Elements[0];
            light1Param = parameters["Lights"].Elements[1];
            light2Param = parameters["Lights"].Elements[2];

            clipPlaneEnabledParam = parameters["ClipPlaneEnabled"];
            clipPlaneParam = parameters["ClipPlane"];         
        }

        /// <summary>
        /// 在施加effect前根据需要重新计算shader参数。
        /// </summary>
        public virtual void OnApply()
        {
            // 重新计算world+view+projection矩阵或雾化矢量
            dirtyFlags = EffectHelpers.SetWorldViewProjAndFog(dirtyFlags, ref world, ref view, ref projection, ref worldView, fogEnabled, fogStart, fogEnd, worldViewProjParam, fogVectorParam);

            // 判断是否需要重新计算材质颜色的diffuse/emissive/alpha参数
            if ((dirtyFlags & EffectDirtyFlags.MaterialColor) != 0)
            {
                EffectHelpers.SetMaterialColor(lightingEnabled, alpha, ref diffuseColor, ref emissiveColor, ref ambientLightColor, diffuseColorParam, emissiveColorParam);

                dirtyFlags &= ~EffectDirtyFlags.MaterialColor;
            }
            if (lightingEnabled)
            {
                // 重新计算世界矩阵的逆转置矩阵和相机位置
                dirtyFlags = EffectHelpers.SetLightingMatrices(dirtyFlags, ref world, ref view, worldParam, worldInverseTransposeParam, eyePositionParam);
            }
        }        
    }
}